home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 November: Tool Chest / Dev.CD Nov 96 TC / Dev.CD Nov 96 TC.toast / Sample Code / Snippets / QuickDraw / DTS Groupies / Source / Groupies.c
Encoding:
C/C++ Source or Header  |  1996-09-17  |  20.2 KB  |  732 lines  |  [TEXT/CWIE]

  1. /*    ________________________________________________________
  2.     ________________________________________________________
  3.  
  4.     DTS Groupies - Dave Hersey 9/18/91 MacDTS
  5.    
  6.     This thingy groups a bunch of pictures together and
  7.     takes them apart using PicComments and a QuickDraw
  8.     bottleneck procedure.  It's an example of how to
  9.     store and retrieve custom PicComments, among other
  10.     things.
  11.    
  12.     Here's how it works:
  13.     
  14.     All pictures are kept in a global structure for easy
  15.     access.  Note that I use the same bounds rectangle
  16.     for all the pictures, but this isn't necessary.
  17.     You should make sure that the enclosing picture is
  18.     wide enough to hold all the others though, or else
  19.     you'll get clipping when they're displayed.
  20.     
  21.     The global structure contains:
  22.     
  23.         • The no. of pictures currently stored.
  24.         • The PicHandles for each of those pictures.
  25.         • The last rectangle each picture was drawn
  26.           in.  (For erase and redraw procedures.)
  27.           
  28.     First, the app creates some individual pictures of
  29.     various objects.  Then it repeatedly groups and
  30.     ungroups them until the user chooses Quit from the
  31.     File menu.
  32.    
  33.     For demonstration purposes, the app moves the pictures
  34.     around as it alternates between grouped and ungrouped
  35.     stages.  This just shows the user that we really have
  36.     separate or consolidated pictures, as the case may be.
  37.     
  38.     Good stuff to look for:
  39.     
  40.         • Example of custom PicComment usage with unique
  41.           identifiers as in Tech Note #181.
  42.           {-> GroupiesPicProc, AssembleGroupies <-}
  43.     
  44.         • Example of a custom QuickDraw bottleneck procedure
  45.           which works in both GrafPorts and CGrafPorts.
  46.           {-> DisassembleGroupies <-}
  47.  
  48.         • Example of finding the deepest device and placing
  49.           a window on it.
  50.           {-> ShowTheGroupies <-}
  51.  
  52.     Lastly, my apologies for all the bad "groupie humor."
  53.  
  54.     {Any similarities to, or resemblance of, actual DTS
  55.      Groupies living or dead is purely coincidental and
  56.      unintentional.}
  57.     ________________________________________________________
  58.     ________________________________________________________    */
  59.  
  60. #include <Dialogs.h>
  61. #include <Fonts.h>
  62. #include <Menus.h>
  63. #include <Devices.h>
  64. #include <Resources.h>
  65.  
  66. /*________________Constants___________________*/
  67.  
  68.  
  69. #define kCreatorType    'EGAD'        /* Our creator type.                */
  70.  
  71.  
  72. #define rMenuBar        128            /* The menubar resource ID.            */
  73.  
  74. #define mApple            128            /* Apple menu ID.                    */
  75.  
  76. #define iAbout            1            /* "About…" menu item index.        */
  77.  
  78. #define mFile            129            /* File menu ID.                    */
  79.  
  80. #define iQuit            1            /* Quit menu item index.            */
  81.  
  82.  
  83. #define kMaxPICTs        50            /* Max. no. of pictures we handle.    */
  84.  
  85. #define kCustomComment    100            /* Custom PicComment indicator.        */
  86.  
  87. #define kSubPICTComment    200            /* Our (sub-picture) sub-PicComment.
  88.                                        This comment indicates that we've
  89.                                        stored a picture inside of a
  90.                                        picture.  We use it to extract
  91.                                        the individual PICTs.            */
  92.  
  93.  
  94. /*__________________Types_____________________*/
  95.  
  96.  
  97. typedef struct TPICTRec {
  98.     int            numPICTs;            /* The number of sub-pictures,         */
  99.     PicHandle    picture[kMaxPICTs];    /* and their PicHandles,            */
  100.     Rect        curPos[kMaxPICTs];    /* and their last drawn positions.    */
  101. } TPICTRec;
  102.  
  103.  
  104. /*_________________Globals____________________*/
  105.  
  106.  
  107. static TPICTRec        gPICTRec;        /* Our global picture record.        */
  108. static Rect            gGroupieBounds;    /* The bounds used by our pictures.    */
  109. static Boolean        gQuitting;        /* "Quitting?" flag.                */
  110. static WindowPtr    gTheWindow;        /* Our window's pointer.            */
  111.  
  112.  
  113. /*_______________Prototypes___________________*/
  114.  
  115. extern void            AssembleGroupies(void);
  116. extern pascal void    GroupiesPicProc(int kind, int dataSize, Handle dataHandle);
  117. extern void            DisassembleGroupies(void);
  118. extern void            DoMenuCommand(long menuResult);
  119. extern void            GetGroupieEvents(void);
  120. extern void            MoveTheGroupies(Rect *wBounds);
  121. extern void            MakeTheGroupies(void);
  122. extern void            ShowTheGroupies(void);
  123.  
  124.  
  125.  
  126. /*    -------------------------------------
  127.     AssembleGroupies groups all the
  128.     pictures in the global picture record
  129.     into one "composite" picture.  It
  130.     removes all the old pictures and
  131.     stores the new one.
  132.     -------------------------------------    */
  133.  
  134. void AssembleGroupies()
  135. {
  136.     PicHandle    aPICT, groupPICT;
  137.     RgnHandle    oldClip;
  138.     int            idx;
  139.     long        dataSize;
  140.     long        ownerApp;
  141.     short        localPicComment;
  142.  
  143. /*    Save the old clipping region, and set a valid one so our groupie
  144.     picture develops ok.                                                */
  145.  
  146.     oldClip = NewRgn();
  147.     GetClip(oldClip);
  148.     ClipRect(&gGroupieBounds);
  149.  
  150.     groupPICT = OpenPicture(&gGroupieBounds);
  151.  
  152.  
  153. /*    Create a picture to contain all the other ones, then draw those into
  154.     it, separated by our PicComments.  Kill the individual pictures as
  155.     we go.  Finally, close the composite picture.                        */
  156.  
  157.     ownerApp = kCreatorType;
  158.     localPicComment = kSubPICTComment;
  159.  
  160.     for(idx = 0; idx < gPICTRec.numPICTs; idx++)
  161.     {
  162.         aPICT = gPICTRec.picture[idx];
  163.  
  164.  
  165. /*    We don't just use a single custom PicComment since another app may
  166.     use the same comment and conflicts could result.  (Not in this app,
  167.     but in the real world.)  Instead, we use the method documented in
  168.     Technical Note #181.  We add six bytes to the handle and store the
  169.     creator type of the app that made the picture followed by 2 bytes
  170.     for a local PicComment kind within the app.  If we used more than
  171.     one PicComment in this app, this extra information would be
  172.     necessary.                                                            */
  173.  
  174.         dataSize = GetHandleSize((Handle) aPICT) +6;
  175.         SetHandleSize((Handle) aPICT, dataSize);
  176.  
  177.         BlockMove((Ptr) *aPICT, (Ptr) *aPICT +6, dataSize -6);
  178.         BlockMove(&ownerApp, (Ptr) *aPICT, 4);
  179.         BlockMove(&localPicComment, (Ptr) *aPICT +4, 2);
  180.  
  181.         PicComment(kCustomComment, dataSize, (Handle) aPICT);
  182.  
  183. /*    Fix the original PicHandle so that we can draw our picture for apps
  184.     that don't know about our custom comments.                            */
  185.  
  186.         BlockMove((Ptr) *aPICT +6, (Ptr) *aPICT, dataSize -6);
  187.         SetHandleSize((Handle) aPICT, dataSize -6);
  188.         DrawPicture(aPICT, &(*aPICT)->picFrame);
  189.         KillPicture(aPICT);
  190.         gPICTRec.picture[idx] = NULL;
  191.     }
  192.  
  193.     ClosePicture();
  194.  
  195.  
  196. /*    Restore the original clipping region and update our global picture
  197.     record so that we have one consolidated picture, in the first slot.
  198.     We set it's current position to (0, 0, 0, 0) so that we don't waste
  199.     time erasing anything on the first draw.                            */
  200.  
  201.     SetClip(oldClip);
  202.     DisposeRgn(oldClip);
  203.  
  204.     gPICTRec.numPICTs = 1;
  205.     gPICTRec.picture[0] = groupPICT;
  206.     SetRect(&gPICTRec.curPos[0], 0, 0, 0, 0);
  207. }
  208.  
  209.  
  210. /*    -------------------------------------
  211.     GroupiesPicProc is our replacement
  212.     for the port's StdCommentProc.
  213.     -------------------------------------    */
  214.  
  215. pascal void GroupiesPicProc(int kind, int dataSize, Handle dataHandle)
  216. {
  217.     int            nextNum;
  218.     long        ownerApp;
  219.     short        localPicComment;
  220.     Handle        theHandle;
  221.  
  222. /*    If this is a custom PicComment, see if it's ours.  In this app,
  223.     we know it always will be, but when you import other pictures
  224.     you can't be so sure.                                                */
  225.  
  226.     if (kind == kCustomComment && (gPICTRec.numPICTs < kMaxPICTs))
  227.     {
  228.         if (dataSize < 6) return;                        /* Not ours?    */
  229.         
  230.         BlockMove((Ptr) *dataHandle, &ownerApp, 4);
  231.         BlockMove((Ptr) *dataHandle +4, &localPicComment, 2);
  232.  
  233.         if ((ownerApp != kCreatorType) ||                /* Not ours?    */
  234.             (localPicComment != kSubPICTComment)) return;
  235.  
  236.  
  237. /*    This is indeed our picture comment.  Create a handle for the data we
  238.     found, store it in our global picture record and bump the number of
  239.     pictures we have.  The reason that we clear the picture's curPos
  240.     rect is so that we won't waste time erasing anything the first time
  241.     we enter MoveTheGroupies.                                            */
  242.  
  243.         nextNum = gPICTRec.numPICTs;
  244.         gPICTRec.picture[nextNum] = (PicHandle) dataHandle;
  245.         SetRect(&gPICTRec.curPos[nextNum], 0, 0, 0, 0);
  246.  
  247.  
  248. /*    After we create the handle for the data, we have to remember that
  249.     we have 6 bytes of identifying "garbage" in front of the picture
  250.     data.  To remove that, BlockMove all the picture data to the
  251.     beginning of the handle and reset the handle's size.  This is kind
  252.     of a hassle, but it's really best to store your custom PicComments
  253.     this way.  Otherwise, you may misinterpret someone elses comments
  254.     or cause them to misinterpret yours.                                */
  255.  
  256.         if (HandToHand((Handle *) &gPICTRec.picture[nextNum]) == noErr)
  257.         {
  258.             ++gPICTRec.numPICTs;
  259.             theHandle = (Handle) gPICTRec.picture[nextNum];
  260.             BlockMove((Ptr) *theHandle +6, (Ptr) *theHandle, dataSize -6);
  261.             SetHandleSize(theHandle, dataSize -6);
  262.         }
  263.     }
  264. }
  265.  
  266.  
  267. /*    -------------------------------------
  268.     DisassembleGroupies ungroups the
  269.     first picture in the global picture
  270.     record.  It replaces that picture
  271.     with new pictures of every picture
  272.     it contained.  All drawing is done
  273.     within another "dummy" picture
  274.     so that nothing draws on the screen.
  275.     The reason we can't use an empty
  276.     clipping region to do this is that
  277.     PicComments will be clipped out along
  278.     with everything else, and we'd be
  279.     hosed.  (We need the PicComments!)
  280.     
  281.     This code is written so that it
  282.     installs the GrafProcs correctly for
  283.     both GrafPorts and CGrafPorts.
  284.     -------------------------------------    */
  285.  
  286. void DisassembleGroupies()
  287. {
  288.     GrafPtr        curPort;
  289.     QDProcs        theQDProcs;        /* If we're using a GrafPort…            */
  290.     CQDProcs    theCQDProcs;    /* If we're using a CGrafPort…            */
  291.     PicHandle    dummyPICT;
  292.  
  293. /*    Reset the number of pictures in our global picture record to zero.
  294.     There's actually one picture there at this point (the composite
  295.     one), but we must set this to zero so that our PicComment handler
  296.     stores extracted pictures in the right place.                        */
  297.  
  298.     gPICTRec.numPICTs = 0;
  299.  
  300.  
  301. /*    Get the current port and the standard QDProcs or CQDProcs,
  302.     depending on whether we have a GrafPort or CGrafPort.                */
  303.  
  304.     GetPort(&curPort);
  305.  
  306.     if (curPort->portBits.rowBytes < 0)                /* CGrafPort…        */
  307.     {
  308.         SetStdCProcs(&theCQDProcs);
  309.         theCQDProcs.commentProc = NewQDCommentProc(GroupiesPicProc);
  310.         curPort->grafProcs = (QDProcsPtr) &theCQDProcs;
  311.     }
  312.     else                                            /* GrafPort…        */
  313.     {
  314.         SetStdProcs(&theQDProcs);
  315.         theQDProcs.commentProc = NewQDCommentProc(GroupiesPicProc);
  316.         curPort->grafProcs = (QDProcsPtr) &theQDProcs;
  317.     }
  318.  
  319.  
  320. /*    Open our dummy picture and draw into it so that our PicComment
  321.     handler is called to parse the picture.  When finished, close the
  322.     picture, kill it and remove our grafProcs.                            */
  323.  
  324.     dummyPICT = OpenPicture(&(*gPICTRec.picture[0])->picFrame);
  325.     DrawPicture(gPICTRec.picture[0], &(*gPICTRec.picture[0])->picFrame);
  326.     ClosePicture();
  327.     KillPicture(dummyPICT);
  328.  
  329.     curPort->grafProcs = NULL;
  330. }
  331.  
  332.  
  333. /*    -------------------------------------
  334.     DoMenuCommand handles our menu items.
  335.     -------------------------------------    */
  336.  
  337. void DoMenuCommand(long menuResult)
  338. {
  339.     int            menuID, menuItem;
  340.     Str255        daName;
  341.     /*MenuHandle    theMenu;*/
  342.     GrafPtr        savePort;
  343.  
  344. /*    Get the menu ID and item ID.            */
  345.  
  346.     menuID = (menuResult >>16) & 0xFFFF;
  347.     menuItem = menuResult & 0xFFFF;
  348.  
  349.  
  350. /*    Do what we're supposed to.                */
  351.  
  352.     switch (menuID)
  353.     {
  354.         case mApple:                        /*    Apple Menu                    */
  355.             switch (menuItem)
  356.             {
  357.                 case iAbout:                /*    -> Handle "About…"            */
  358.                     break;
  359.  
  360.                 default:                    /*    -> The rest are DAs.        */
  361.  
  362.                     GetPort(&savePort);
  363.                     GetMenuItemText(GetMenuHandle(mApple), menuItem, daName);
  364.                     OpenDeskAcc((ConstStr255Param)daName);
  365.                     SetPort(savePort);
  366.                     break;
  367.             };
  368.  
  369.         case mFile:                            /*    File Menu                    */
  370.             switch (menuItem)
  371.             {
  372.                 case iQuit:
  373.                     gQuitting = true;        /*    -> Quit                        */
  374.                     break;
  375.             }
  376.  
  377.     }
  378.  
  379.     HiliteMenu(0);
  380.  
  381. }
  382.  
  383.  
  384. /*    -------------------------------------
  385.     GetGroupieEvents is a main event
  386.     loop.  It calls WaitNextEvent and
  387.     other nice stuff.  It also makes our
  388.     Groupies assemble, disassemble, and
  389.     hop about.
  390.     -------------------------------------    */
  391.  
  392. void GetGroupieEvents()
  393. {
  394.     EventRecord        theEvent;
  395.     WindowPtr        whichWindow;
  396.     short            partCode;
  397.     Rect            dragRect;
  398.     RgnHandle        grayRgn;
  399.     char            key, time;
  400.     long            finalTicks;
  401.  
  402.  
  403. /*    Set up the rectangle for where we can drag windows.  Initialize
  404.     our "time-through-the-loop" counter to -1 so that it gets bumped
  405.     to zero on the first pass.  This will enable us assemble the
  406.     grouped picture as we go through the first time.                        */
  407.  
  408.     grayRgn = GetGrayRgn();
  409.     dragRect = (*grayRgn)->rgnBBox;
  410.     time = -1;
  411.  
  412.  
  413. /*    We have a counter which goes from 0-26 and is incremented each time
  414.     we go through this code. At time = 0, We assemble the grouped image.
  415.     At time = 12, we break all the PICTs out of it.  At time = 27, we
  416.     cycle back to time = 0.  In between these life altering times,
  417.     (at least for groupies), we draw all of our current pictures in
  418.     random places.  This clearly shows whether the PICTs are currently
  419.     grouped or not.  We go through this loop until the user quits.            */
  420.  
  421.     do
  422.     {
  423.         SetPort(gTheWindow);
  424.         time = ++time % 27;
  425.     
  426.          if (time == 0)
  427.         {
  428.             AssembleGroupies();            /*    Group the pictures.                */
  429.             EraseRect(&(gTheWindow)->portRect);
  430.         }
  431.  
  432.          if (time == 12)
  433.         {
  434.             DisassembleGroupies();        /*    Ungroup the pictures.            */
  435.             EraseRect(&(gTheWindow)->portRect);
  436.         }
  437.  
  438.  
  439. /*    Move all pictures so we can see their current state.                    */
  440.  
  441.         MoveTheGroupies(&(gTheWindow)->portRect);
  442.  
  443.  
  444. /*    Delay so our graphics don't flash.                                        */
  445.  
  446.         Delay((time < 12)? 40:10, &finalTicks);
  447.     
  448.  
  449. /*    Handle any pending events.                                                */
  450.  
  451.         if (WaitNextEvent(everyEvent, &theEvent, 0, NULL))
  452.             switch (theEvent.what)
  453.             {
  454.                 case mouseDown:                    /*    Handle mouse clicks.    */
  455.                 
  456.                     partCode = FindWindow(theEvent.where, &whichWindow);
  457.  
  458.                     switch (partCode)
  459.                     {
  460.                         case inContent:
  461.                             if (whichWindow != FrontWindow())
  462.                                 SelectWindow(whichWindow);
  463.                             break;
  464.     
  465.                         case inDrag:
  466.                             DragWindow(whichWindow, theEvent.where, &dragRect);
  467.                             break;
  468.     
  469.                         case inMenuBar:
  470.                             DoMenuCommand(MenuSelect(theEvent.where));
  471.     
  472.                         case inSysWindow:
  473.                             SystemClick(&theEvent, whichWindow);
  474.                             break;
  475.                     }
  476.                     break;
  477.     
  478.                 case updateEvt:                    /*    Handle update events.    */
  479.                         BeginUpdate((WindowPtr) theEvent.message);
  480.                         EndUpdate((WindowPtr) theEvent.message);
  481.                     break;
  482.  
  483.                 case keyDown:                    /*    Handle key presses.        */
  484.                 case autoKey: 
  485.                     key = (char) (theEvent.message & charCodeMask);
  486.                     if (((theEvent.modifiers & cmdKey) != 0) && (theEvent.what == keyDown))
  487.                         DoMenuCommand(MenuKey(key));
  488.                     break;
  489.             }
  490.     }
  491.     while (!gQuitting);
  492. }
  493.  
  494.  
  495. /*    -------------------------------------
  496.     MoveTheGroupies moves the current
  497.     pictures… somewhere randomly.  It
  498.     first erases all the pictures in
  499.     descending order.  Then it redraws
  500.     them in new locations in ascending
  501.     order. This way we don't wipe out
  502.     any of the new pictures when the old
  503.     ones are erased.
  504.     -------------------------------------    */
  505.  
  506. void MoveTheGroupies(Rect *wBounds)
  507. {
  508.     int        newLeft, newTop, width, height, idx;
  509.     float    maxX, maxY;
  510.     Rect    picFrame, curPos;
  511.  
  512. /*    First erase all pictures in reverse order.  Also, calculate their
  513.     new locations and store those in their curPos fields.                */
  514.  
  515.     for (idx = gPICTRec.numPICTs -1; idx >= 0; idx--)
  516.     {
  517.         curPos = gPICTRec.curPos[idx];
  518.         EraseRect(&curPos);
  519.  
  520.         picFrame = (*gPICTRec.picture[idx])->picFrame;
  521.         width = picFrame.right -picFrame.left;
  522.         height = picFrame.bottom -picFrame.top;
  523.  
  524.  
  525. /*    To calculate new positions, we find the maximum position we can
  526.     have for the picture's top left corner.  Then, we find a random
  527.     point that's bounded by (0, 0) and that maximum.  Finally, we
  528.     set this picture's current position so that it has this point for
  529.     its top left corner.                                                */
  530.  
  531.         maxX = (wBounds->right - wBounds->left) -width;
  532.         maxY = (wBounds->bottom - wBounds->top) -height;
  533.         
  534.         newTop = (((float) Random() +32767)/65534.0) * maxX;
  535.         newLeft = (((float) Random() +32767)/65534.0) * maxY;
  536.     
  537.         curPos.top = newTop;
  538.         curPos.left = newLeft;
  539.         curPos.bottom = newTop +height;
  540.         curPos.right = newLeft +width;
  541.         gPICTRec.curPos[idx] = curPos;
  542.     }
  543.  
  544.  
  545. /*    Now draw all the pictures in their new positions.                    */
  546.  
  547.     for (idx = 0; idx < gPICTRec.numPICTs; idx++)
  548.         DrawPicture(gPICTRec.picture[idx], &gPICTRec.curPos[idx]);
  549. }
  550.  
  551.  
  552. /*    -------------------------------------
  553.     MakeTheGroupies creates the pictures
  554.     that will be grouped.  These can be
  555.     any QuickDraw pictures.  For this
  556.     example, I use four pictures; one
  557.     containing a square, one with a
  558.     circle, one with a triangle and one
  559.     with some text.  These are all stored
  560.     in the global picture record.
  561.     
  562.     This routine is only called once, to
  563.     put some pictures into the works to
  564.     start with.
  565.     -------------------------------------    */
  566.  
  567. void MakeTheGroupies()
  568. {
  569.     RgnHandle    oldClip;
  570.     PolyHandle    trianglePoly;
  571.     int            fNum, vPos;
  572.  
  573. /*    Save the current clipping region so that we can restore it later.
  574.     Set our own clipping region, so that we know we have a valid one.
  575.     Also initialize the number of pictures in our global picture
  576.     structure to zero.                                                    */
  577.  
  578.     oldClip = NewRgn();
  579.     GetClip(oldClip);
  580.  
  581.     SetRect(&gGroupieBounds, 0, 0, 150, 150);
  582.     ClipRect(&gGroupieBounds);
  583.     
  584.     gPICTRec.numPICTs = 0;
  585.  
  586.  
  587. /*    Create a picture with a blue square in it.  We set the curPos
  588.     rectangle for all of these pictures to (0, 0, 0, 0) so that
  589.     we don't do any unnecessary erasing the first time they enter
  590.     MoveTheGroupies.                                                    */
  591.  
  592.     gPICTRec.picture[0] = OpenPicture(&gGroupieBounds);
  593.     ForeColor(blueColor);
  594.     PaintRect(&gGroupieBounds);
  595.     ClosePicture();
  596.     SetRect(&gPICTRec.curPos[0], 0, 0, 0, 0);
  597.     ++gPICTRec.numPICTs;
  598.  
  599.  
  600. /*    Create a picture with a red circle in it.                            */
  601.  
  602.     gPICTRec.picture[1] = OpenPicture(&gGroupieBounds);
  603.     ForeColor(redColor);
  604.     PaintOval(&gGroupieBounds);
  605.     ClosePicture();
  606.     SetRect(&gPICTRec.curPos[1], 0, 0, 0, 0);
  607.     ++gPICTRec.numPICTs;
  608.  
  609.  
  610. /*    Create a picture with a green triangle in it.    */
  611.  
  612.     gPICTRec.picture[2] = OpenPicture(&gGroupieBounds);
  613.     ForeColor(greenColor);
  614.  
  615.     trianglePoly = OpenPoly();
  616.     MoveTo(gGroupieBounds.left, gGroupieBounds.bottom);
  617.     LineTo((gGroupieBounds.right - gGroupieBounds.left)/2, gGroupieBounds.top);
  618.     LineTo(gGroupieBounds.right, gGroupieBounds.bottom);
  619.     LineTo(gGroupieBounds.left, gGroupieBounds.bottom);
  620.     ClosePoly();
  621.  
  622.     PaintPoly(trianglePoly);
  623.     KillPoly(trianglePoly);
  624.     ClosePicture();
  625.     SetRect(&gPICTRec.curPos[2], 0, 0, 0, 0);
  626.     ++gPICTRec.numPICTs;
  627.  
  628.  
  629. /*    Create a picture with some text in it.    */
  630.  
  631.     gPICTRec.picture[3] = OpenPicture(&gGroupieBounds);
  632.     ForeColor(blackColor);
  633.  
  634.     GetFNum((ConstStr255Param) "\pTimes", (short*)&fNum);
  635.     TextFont(fNum);
  636.     TextSize(12);
  637.     TextFont(bold);
  638.     vPos = gGroupieBounds.top +(gGroupieBounds.bottom - gGroupieBounds.top)/2;
  639.     MoveTo(gGroupieBounds.left +10, vPos +10);
  640.     DrawString((ConstStr255Param) "\pWe are the Groupies!");
  641.     ClosePicture();
  642.     SetRect(&gPICTRec.curPos[3], 0, 0, 0, 0);
  643.     ++gPICTRec.numPICTs;
  644.  
  645.  
  646. /*    Restore the original clipping region.                                */
  647.  
  648.     SetClip(oldClip);
  649.     DisposeRgn(oldClip);
  650. }
  651.  
  652.  
  653. /*    -------------------------------------
  654.     ShowTheGroupies starts the show.
  655.     First, we find the deepest display
  656.     because the groupies are a colorful
  657.     bunch.  Then we create a window and
  658.     some Groupies.  Finally, we jump into
  659.     our main event loop.
  660.     -------------------------------------    */
  661.  
  662. void ShowTheGroupies()
  663. {
  664.     Rect        maxRect, deepRect, wBounds;
  665.     GDHandle    deepGDH;
  666.  
  667. /*    Find the bounds of the deepest device.  We'll use this to determine
  668.     where to put our window.  Passing the maximum enclosing rectangle
  669.     to GetMaxDevice assures that we find the deepest device available.    */
  670.  
  671.     SetRect(&maxRect, -32767, -32767, 32767, 32767);
  672.     deepGDH = GetMaxDevice(&maxRect);
  673.     deepRect = (*deepGDH)->gdRect;
  674.  
  675.  
  676. /*    Create a window for our drawing, offset onto the deepest device.    */
  677.     
  678.     SetRect(&wBounds, 40, 40, 360, 340);
  679.     OffsetRect(&wBounds, wBounds.left +deepRect.left, wBounds.top +deepRect.top);
  680.  
  681.     gTheWindow = NewWindow(nil, &wBounds, (ConstStr255Param) "\pDTS Groupies!", true, noGrowDocProc, (WindowPtr) -1, false, 1234);
  682.     SetPort(gTheWindow);
  683.  
  684.  
  685. /*    Create our pictures to group, then go into the work loop.  This
  686.     loop continually groups the pictures, draws the grouped picture
  687.     in different locations, ungroups the picture, draws the ungrouped
  688.     pictures in different locations and repeats until the user quits.    */
  689.  
  690.     MakeTheGroupies();
  691.     GetGroupieEvents();
  692. }
  693.  
  694.  
  695. /*    -------------------------------------
  696.     main.
  697.     -------------------------------------    */
  698.  
  699. void main(void)
  700. {    
  701.     unsigned long randSeed;
  702.     Handle        menuBar;
  703.  
  704. /*    Initialize the toolbox routines.                                    */
  705.  
  706.     InitGraf(&qd.thePort);
  707.     InitFonts();
  708.     InitWindows();
  709.     InitMenus();
  710.     InitDialogs(nil);
  711.     InitCursor();
  712.  
  713.  
  714. /*    Set up our menubar.                                                    */
  715.  
  716.     menuBar = GetNewMBar(rMenuBar);        /*    Read menus into menu bar    */
  717.     SetMenuBar(menuBar);                /*    and install them.            */
  718.     DisposeHandle(menuBar);
  719.     
  720.     AppendResMenu(GetMenuHandle(mApple), 'DRVR');
  721.     DrawMenuBar();
  722.  
  723.  
  724. /*    Initialize the random number seed for our hopping groupies and set
  725.     our quitting flag to false.  Call the routine that runs everything,
  726.     then, quit.                                                            */
  727.  
  728.     GetDateTime(&randSeed);
  729.     gQuitting = false;
  730.     ShowTheGroupies();
  731. }
  732.